#pragma once
#include "log.hpp"
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <cstring>
#include <memory>
#include <mutex>
#include <jsoncpp/json.h>

namespace tr
{

class tcp_socket;
namespace Protocol
{ 
    extern int recv(tcp_socket&,Json::Value&); 
    extern int send(const tcp_socket&,const Json::Value&);
} // end of Protocol

/* othrer sockaddr -> sockaddr */
#define COV(addr) ((sockaddr*)(&addr))

// 监听等待队列的大小
static const int lis_block_size = 5;
class tcp_socket
{
    friend int Protocol::recv(tcp_socket&,Json::Value&);
    friend int Protocol::send(const tcp_socket&,const Json::Value&);
    using sock_ptr = std::unique_ptr<tcp_socket>;

    sockaddr_in _m_addr;
    socklen_t _m_len;
    int _m_sockfd;
    std::string _m_buffer;  // 用户的输入输出缓冲区

    std::mutex _m_recv_mt;
    std::mutex _m_send_mt;
public:
    tcp_socket() // 初始化 socket
    {
        // 创建 sokcet
        _m_sockfd = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        _m_len = sizeof(sockaddr_in);
        if(_m_sockfd < 0)
        {
            log(ERROR) << "Bulid listen socket error." << std::endl;
            exit(0);
        }
        int fg = 1;
    }

    // 绑定 ip + 端口的初始化
    tcp_socket(uint16_t port,in_addr_t ip) // 委托构造函数
        :tcp_socket()
    {
        ::memset(&_m_addr,0,sizeof(_m_addr));
        _m_addr.sin_family = AF_INET;
        _m_addr.sin_port = port;
        _m_addr.sin_addr.s_addr = ip;
        _m_len = sizeof(_m_addr);
    }

    int bind() noexcept
    {
        // 绑定 ip和端口
        if(::bind(_m_sockfd,COV(_m_addr),_m_len))
        {
            log(ERROR) << "Bind socket errror." << std::endl;
            return -1;
        }
        return 0;
    }

    int listen() noexcept
    {
        // 监听指定套接字
        if(::listen(_m_sockfd,lis_block_size))
        {
            log(ERROR) << "Listen socket error." << std::endl;
            return -1;
        }
        return 0;
    }

    sock_ptr accept() noexcept
    {
        sock_ptr _new_sock(new tcp_socket());
        _new_sock->_m_len = sizeof(sockaddr_in);
        _new_sock->_m_sockfd = 
            ::accept(_m_sockfd,COV(_new_sock->_m_addr),&_new_sock->_m_len);
        if(_new_sock->_m_sockfd < 0)
        {
            log(WARNING) << 
                "Accept user or Bulid user's socket error." << std::endl;
            return nullptr;
        }
        return _new_sock;
    }

    // 获取点分十进制的ip
    std::string get_ip() noexcept
    {
        char buffer[128] = {0};
        inet_ntop(AF_INET,&(_m_addr.sin_addr),buffer,sizeof(buffer));
        return buffer;
    }

    uint16_t get_port() noexcept
    { return ntohs(_m_addr.sin_port); }

    int get_sockfd() noexcept
    {return _m_sockfd;}

    sockaddr* get_sockaddr() noexcept
    { return COV(_m_addr);}

    socklen_t get_socklen() noexcept
    { return _m_len;}

    std::string addr_info()
    {
        return "[IP:" + get_ip() + ",Port:" + std::to_string(get_port()) + "]";
    }


    /**
     * @brief 向该 socket 发送数据，线程安全，每一时刻只能有一个线程从该 socket 发数据
     * @param msg 发送的 JSON 数据
     * @return 返回发送的数据大小，出现错误返回 -1
    */
    int send(const Json::Value& msg)
    {
        std::lock_guard<std::mutex> lg(_m_send_mt);
        return Protocol::send(*this,msg);
    }

    /**
     * @brief 接收用户发来的信息，线程安全，每一时刻只能一个线程从该 socket 收数据
     * @param msg 输出型参数，返回用户输入
     * @return 接收成功返回 1，信息接收错误返回 -1，连接断开返回 0。
    */
    int recv(Json::Value& msg)
    {
        std::lock_guard<std::mutex> lg(_m_recv_mt);
        return Protocol::recv(*this,msg);
    }
};



}   // tr end

